# Copyright (C) Kumo inc. and its affiliates.
# Author: Jeff.li lijippy@163.com
# All rights reserved.
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU Affero General Public License as published
# by the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
# GNU Affero General Public License for more details.
#
# You should have received a copy of the GNU Affero General Public License
# along with this program.  If not, see <https:#www.gnu.org/licenses/>.
#
include_guard(GLOBAL)
function(get_rpath_origin VAR)
  if(APPLE)
    set(_origin @loader_path)
  else()
    set(_origin "\$ORIGIN")
  endif()
  set(${VAR}
      ${_origin}
      PARENT_SCOPE)
endfunction()

function(pypollux_add_module TARGET)
  pybind11_add_module(${TARGET} ${ARGN})

  if(DEFINED SKBUILD_PROJECT_VERSION_FULL)
    target_compile_definitions(
      ${TARGET} PRIVATE PYPOLLUX_VERSION=${SKBUILD_PROJECT_VERSION_FULL})
  else()
    target_compile_definitions(${TARGET} PRIVATE PYPOLLUX_VERSION=dev)
  endif()

  # Set the rpath so linker looks within pypollux package for libs
  get_rpath_origin(_origin)
  set_target_properties(
    ${TARGET} PROPERTIES INSTALL_RPATH "${_origin}/;${CMAKE_BINARY_DIR}/lib"
                         INSTALL_RPATH_USE_LINK_PATH TRUE)
  install(TARGETS ${TARGET} LIBRARY DESTINATION pypollux
                                    COMPONENT pypollux_libraries)
endfunction()

# TODO use file sets
function(pollux_install_library_headers)
  # Find any headers and install them relative to the source tree in include.
  file(GLOB _hdrs "*.h")
  if(NOT "${_hdrs}" STREQUAL "")
    cmake_path(
      RELATIVE_PATH
      CMAKE_CURRENT_SOURCE_DIR
      BASE_DIRECTORY
      "${CMAKE_SOURCE_DIR}"
      OUTPUT_VARIABLE
      _hdr_dir)
    install(FILES ${_hdrs} DESTINATION include/${_hdr_dir})
  endif()
endfunction()

# Base add pollux library call to add a library and install it.
function(pollux_base_add_library TARGET)
  add_library(${TARGET} ${ARGN})
  install(TARGETS ${TARGET} DESTINATION lib/pollux)
  pollux_install_library_headers()
endfunction()

# This is extremely hackish but presents an easy path to installation.
function(pollux_add_library TARGET)
  set(options OBJECT STATIC SHARED INTERFACE)
  set(oneValueArgs)
  set(multiValueArgs)
  cmake_parse_arguments(
    POLLUX
    "${options}"
    "${oneValueArgs}"
    "${multiValueArgs}"
    ${ARGN})

  # Remove library type specifiers from ARGN
  set(library_type)
  if(POLLUX_OBJECT)
    set(library_type OBJECT)
  elseif(POLLUX_STATIC)
    set(library_type STATIC)
  elseif(POLLUX_SHARED)
    set(library_type SHARED)
  elseif(POLLUX_INTERFACE)
    set(library_type INTERFACE)
  endif()

  list(REMOVE_ITEM ARGN OBJECT)
  list(REMOVE_ITEM ARGN STATIC)
  list(REMOVE_ITEM ARGN SHARED)
  list(REMOVE_ITEM ARGN INTERFACE)
  # Propagate to the underlying add_library and then install the target.
  if(POLLUX_MONO_LIBRARY)
    if(TARGET pollux)
      # Target already exists, append sources to it.
      target_sources(pollux PRIVATE ${ARGN})
      install(TARGETS pollux LIBRARY DESTINATION pypollux
                                    COMPONENT pypollux_libraries)
    else()
      set(_type STATIC)
      if(POLLUX_BUILD_SHARED)
        set(_type SHARED)
      endif()
      # Create the target if this is the first invocation.
      add_library(pollux ${_type} ${ARGN})
      set_target_properties(pollux PROPERTIES LIBRARY_OUTPUT_DIRECTORY
                                             ${PROJECT_BINARY_DIR}/lib)
      set_target_properties(pollux PROPERTIES ARCHIVE_OUTPUT_DIRECTORY
                                             ${PROJECT_BINARY_DIR}/lib)
      install(TARGETS pollux DESTINATION lib/pollux)
    endif()
    # create alias for compatability
    if(NOT TARGET ${TARGET})
      add_library(${TARGET} ALIAS pollux)
    endif()
  else()
    # Create a library for each invocation.
    pollux_base_add_library(${TARGET} ${library_type} ${ARGN})
  endif()
  pollux_install_library_headers()
endfunction()

function(pollux_link_libraries TARGET)
  # TODO(assignUser): Handle scope keywords (they currently are empty calls ala
  # target_link_libraries(target PRIVATE))
  if(POLLUX_MONO_LIBRARY)
    # These targets follow the pollux_* name for consistency but are NOT actually
    # aliases to pollux when building the mono lib and need to be linked
    # explicitly (this is a hack)
    set(explicit_targets
        pollux_exec_test_lib
        # see pollux/experimental/wave/README.md
        pollux_wave_common
        pollux_wave_decode
        pollux_wave_dwio
        pollux_wave_exec
        pollux_wave_stream
        pollux_wave_vector)

    foreach(_arg ${ARGN})
      list(FIND explicit_targets ${_arg} _explicit)
      if(_explicit EQUAL -1 AND "${_arg}" MATCHES "^pollux_*")
        message(DEBUG "\t\tDROP: ${_arg}")
      else()
        message(DEBUG "\t\tADDING: ${_arg}")
        target_link_libraries(pollux ${_arg})
      endif()
    endforeach()
  else()
    target_link_libraries(${TARGET} ${ARGN})
  endif()
endfunction()

function(pollux_include_directories TARGET)
  if(POLLUX_MONO_LIBRARY)
    target_include_directories(pollux ${ARGN})
  else()
    target_include_directories(${TARGET} ${ARGN})
  endif()
endfunction()

function(pollux_compile_definitions TARGET)
  if(POLLUX_MONO_LIBRARY)
    target_compile_definitions(pollux ${ARGN})
  else()
    target_compile_definitions(${TARGET} ${ARGN})
  endif()
endfunction()

function(pollux_sources TARGET)
  if(POLLUX_MONO_LIBRARY)
    target_sources(pollux ${ARGN})
  else()
    target_sources(${TARGET} ${ARGN})
  endif()
endfunction()
